using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Reflection;
using HybridCLR;
using UnityEngine;
using YooAsset;
using System.Net.Sockets;
using System.Text.RegularExpressions;
using UnityEngine.UI;
using Codice.Client.BaseCommands;

public static class CoroutubeExtensions
{
    public static IEnumerator WaitAll(this MonoBehaviour mono, params IEnumerator[] enumerators)
    {
        return enumerators.Select(mono.StartCoroutine).ToArray().GetEnumerator();
    }
}

public class AppStep : MonoBehaviour
{
    public Text displayTxt;
    public Slider slider;
    List<string> PackageList = new List<string>()
    {
        "Dll",
        "Main"
    };
    public static Dictionary<string, AppResUpdate> Packages;
    private void Awake()
    {
        Packages = new Dictionary<string, AppResUpdate>();
        YooAssets.Initialize();
        for (int i = 0; i < PackageList.Count; i++)
        {
            Packages.Add(PackageList[i], new AppResUpdate(PackageList[i], this));
        }

    }

    public EPlayMode playMode = EPlayMode.EditorSimulateMode;
    // Start is called before the first frame update
    void Start()
    {
        foreach (var item in Packages)
        {
            item.Value.Create();
        }
        StartCoroutine(WaitYooAssetsHotUpdate());
    }

    void SetProgressStr(string str)
    {
        displayTxt.text = str;
    }

    IEnumerator UpdateProgress(params AsyncOperationBase[] asyncOperations)
    {
        float max = 0;
        float currentProgress = 0;
        foreach(var item in asyncOperations)
        {
            max += 1;
        }

        bool isAllDone = false;

        do
        {
            bool currentDone = true;
            foreach (var item in asyncOperations)
            {
                currentProgress += item.Progress;
                if (!item.IsDone)
                {
                    currentDone = false;
                }
            }
            if (currentDone)
            {
                isAllDone = true;
            }
            slider.value = currentProgress/max;
            yield return null;
        } while (!isAllDone);
 
        
    }
    IEnumerator WaitYooAssetsHotUpdate()
    {
        SetProgressStr("初始化中");
        ///等待初始化
        List<IEnumerator> list = new List<IEnumerator>();
        List<AsyncOperationBase> ops = new List<AsyncOperationBase> ();

        ops.Clear ();
        foreach (var item in Packages)
        {
            ops.Add(item.Value.operationBase);
            list.Add(item.Value.InitYooAssets());
        }
        StartCoroutine(UpdateProgress());
        yield return this.WaitAll(list.ToArray());

        int error = 0;
        foreach (var item in Packages)
        {
            if (item.Value.ErrorCode != UpdateErrorCode.None)
            {
                error = 1;
            }
        }
        if (error != 0)
            yield break;

        list.Clear();

        foreach (var item in Packages)
        {
            list.Add(item.Value.RequestPackageVersion());
        }
        yield return this.WaitAll(list.ToArray());
        foreach (var item in Packages)
        {
            if (item.Value.ErrorCode != UpdateErrorCode.None)
            {
                error = 1;
            }
        }
        if (error != 0)
            yield break;

        list.Clear();

        foreach (var item in Packages)
        {
            list.Add(item.Value.UpdatePackageManifest());
        }
        yield return this.WaitAll(list.ToArray());
        foreach (var item in Packages)
        {
            if (item.Value.ErrorCode != UpdateErrorCode.None)
            {
                error = 1;
            }
        }
        if (error != 0)
            yield break;

        list.Clear();
       
        foreach (var item in Packages)
        {
            item.Value.CreateDownLoader();
        }
        long needUpdateBytes = 0;
        foreach (var item in Packages)
        {
            if (item.Value.Downloader.TotalDownloadCount >0)
            {
                needUpdateBytes += item.Value.Downloader.TotalDownloadBytes;
            }
            
        }

        if (needUpdateBytes > 0)
        {
            foreach (var item in Packages)
            {
                if (item.Value.Downloader.TotalDownloadCount > 0)
                {
                    list.Add(item.Value.StartDownload());
                }
            }
        }
        if (list.Count > 0)
        {
            yield return this.WaitAll(list.ToArray());
        }
        if (error != 0)
            yield break;
        yield return this.WaitAll(StartLoadDllData(StartGame));
    }


    List<string> hotUpdateDllNames = new List<string>()
    {
        "GameFramework.dll.bytes",
        "HotUpdate.dll.bytes",
    };


    static string GetPath()
    {
#if UNITY_ANDROID
        return "Android";
#else
      if (Application.platform== RuntimePlatform.Android)
            {
                return "Android";
            }
            return "Windows";
#endif

    }



    #region Hybird 热更新
    private static List<string> AOTMetaAssemblyFiles { get; } = new List<string>()
    {
        "mscorlib.dll.bytes",
        "System.dll.bytes",
        "System.Core.dll.bytes",
    };
    private static Dictionary<string, byte[]> s_assetDatas = new Dictionary<string, byte[]>();

    public static byte[] ReadBytesFromStreamingAssets(string dllName)
    {
        return s_assetDatas[dllName];
    }
    /// <summary>
    /// 为aot assembly加载原始metadata， 这个代码放aot或者热更新都行。
    /// 一旦加载后，如果AOT泛型函数对应native实现不存在，则自动替换为解释模式执行
    /// </summary>
    private static void LoadMetadataForAOTAssemblies()
    {
        /// 注意，补充元数据是给AOT dll补充元数据，而不是给热更新dll补充元数据。
        /// 热更新dll不缺元数据，不需要补充，如果调用LoadMetadataForAOTAssembly会返回错误
        /// 
        HomologousImageMode mode = HomologousImageMode.SuperSet;
        foreach (var aotDllName in AOTMetaAssemblyFiles)
        {
            byte[] dllBytes = ReadBytesFromStreamingAssets(aotDllName);
            // 加载assembly对应的dll，会自动为它hook。一旦aot泛型函数的native函数不存在，用解释器版本代码
            LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
            Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
        }
    }

    IEnumerator StartLoadDllData(Action onDownloadComplete)
    {
        var assets = new List<string>
        {
            "HotUpdateMain.dll.bytes",
        }.Concat(AOTMetaAssemblyFiles);

        foreach (var asset in assets)
        {
            var handle = Packages["Dll"].Package.LoadAssetAsync<TextAsset>("Assets/UpdateAssets/" + asset);
            yield return handle;
            s_assetDatas[asset] = (handle.AssetObject as TextAsset).bytes;
        }
        onDownloadComplete();
    }
    private static Assembly _hotUpdateAss;
    void StartGame()
    {
        LoadMetadataForAOTAssemblies();
        if (playMode == EPlayMode.EditorSimulateMode)
        {
            _hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdateMain");
        }
        else
        {
            _hotUpdateAss = Assembly.Load(ReadBytesFromStreamingAssets("HotUpdateMain.dll.bytes"));
        }
        Type entryType = _hotUpdateAss.GetType("AppStart");
        entryType.GetMethod("Start").Invoke(null, null);
    }

    #endregion

}

class RemoteServices : IRemoteServices
{
    private readonly string _defaultHostServer;
    private readonly string _fallbackHostServer;

    public RemoteServices(string defaultHostServer, string fallbackHostServer)
    {
        _defaultHostServer = defaultHostServer;
        _fallbackHostServer = fallbackHostServer;
    }
    string IRemoteServices.GetRemoteMainURL(string fileName)
    {
        return $"{_defaultHostServer}/{fileName}";
    }
    string IRemoteServices.GetRemoteFallbackURL(string fileName)
    {
        return $"{_fallbackHostServer}/{fileName}";
    }
}
